package com.globallogic.jira.gadget.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.atlassian.jira.ComponentManager;
import com.atlassian.jira.config.SubTaskManager;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.link.IssueLink;
import com.atlassian.jira.issue.link.IssueLinkManager;
import com.atlassian.jira.issue.search.SearchException;
import com.atlassian.jira.project.Project;
import com.atlassian.templaterenderer.TemplateRenderer;
import com.globallogic.jira.gadget.composite.ReportNode;
import com.globallogic.jira.gadget.renderer.TreeRenderer;
import com.globallogic.jira.gadget.renderer.TreeRendererForGadget;
import com.globallogic.jira.gadget.renderer.TreeRendererForReport;
import com.globallogic.jira.gadget.utils.Search;

/**
 * 
 * @author anton.pereverziev
 * @author egor.chymakov
 * 
 */
@SuppressWarnings("serial")
public class TreeBuilderServlet extends HttpServlet {

    private final int MAX_NESTING_LEVEL = 10;
    private static final Logger log = Logger
	    .getLogger(CreatingTypesListener.class);
    private final IssueLinkManager issueLinkManager;

    /**
     * Servlet constructor that is auto-wired by Spring to include the following
     * services registered in the params.
     * 
     * @param templateRenderer
     */
    public TreeBuilderServlet(TemplateRenderer templateRenderer,
	    IssueLinkManager issueLinkManager) {
	this.issueLinkManager = issueLinkManager;
    }

    /**
	 * 
	 */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
	    throws ServletException, IOException {
	String projectName = req.getParameter("projectName");
	String issueType = req.getParameter("issueType");
	String fixVersion = req.getParameter("fixVersion");
	String linkType = req.getParameter("linkType");
	String includeSubtasks = req.getParameter("includeSubtasks");
	String includeOrphans = req.getParameter("includeOrphans");
	String renderer = req.getParameter("renderer");

	if (log.isDebugEnabled()) {
	    log.debug("********************************");
	    log.debug("selected project =" + projectName);
	    log.debug("issueType =" + issueType);
	    log.debug("fixVersion =" + fixVersion);
	    log.debug("linkType =" + linkType);
	    log.debug("includeSubtasks =" + includeSubtasks);
	    log.debug("includeOrphans =" + includeOrphans);
	    log.debug("renderer =" + renderer);
	}

	Project project = ComponentManager.getInstance().getProjectManager()
		.getProjectObjByName(projectName);

	Long pid = null;
	if (project != null) {
	    pid = project.getId();
	} else {
	    if (log.isDebugEnabled())
		log.debug("Project = null");
	    return;
	}

	List<Issue> all = null;
	try {
	    all = Search.getIssuesFromProject(pid, issueType, fixVersion,
		    Boolean.parseBoolean(includeOrphans));
	} catch (SearchException e) {
	    e.printStackTrace();
	}
	ReportNode root = buildReport(all, linkType,
		Boolean.parseBoolean(includeSubtasks));
	TreeRenderer rendererImpl = (renderer.equals("gadget") ? new TreeRendererForGadget()
		: new TreeRendererForReport());
	resp.setContentType("text/html");
	PrintWriter writer = resp.getWriter();
	writer.print(rendererImpl.getHTMLHead(projectName, issueType,
		fixVersion, linkType, includeSubtasks));
	writer.print(getHTMLTree(root, rendererImpl));
	writer.flush();
	writer.close();
    }

    /**
     * Build a tree like structure from list of issue, filter the issues by
     * types, so only correct hierarchy will be rendered.
     * 
     * @param all
     * @return
     */

    private ReportNode buildReport(List<Issue> issues, String issueLinkType,
	    boolean includeSubTasks) {
	if (log.isDebugEnabled())
	    log.debug("Building report");
	ReportNode root = new ReportNode(null);
	for (Issue issue : issues) {
	    root.addNode(buildBranch(issue, 0, issueLinkType, includeSubTasks));
	}
	return root;
    }

    /**
     * Collects data and builds tree
     * 
     * @param root
     * @param level
     * @param issueLinkType
     * @param includeSubTasks
     * @return
     */

    private ReportNode buildBranch(Issue root, int level, String issueLinkType,
	    boolean includeSubTasks) {
	ReportNode node = new ReportNode(root);
	if (level > MAX_NESTING_LEVEL)
	    return node;
	List<IssueLink> links = issueLinkManager.getInwardLinks(root.getId());
	for (IssueLink issueLink : links) {
	    if (issueLink.getIssueLinkType().getName().equals(issueLinkType)
		    || issueLinkType.equals("All")) {
		Issue issue = issueLink.getSourceObject();
		node.addNode(buildBranch(issue, level + 1, issueLinkType,
			includeSubTasks));
	    }
	}
	if (includeSubTasks) {

	    SubTaskManager subTaskManager = ComponentManager.getInstance()
		    .getSubTaskManager();
	    List<IssueLink> subTasks = subTaskManager.getSubTaskIssueLinks(root
		    .getId());
	    log.debug("Getting subtasks; size: " + subTasks.size());
	    for (IssueLink issueLink : subTasks) {
		Issue issue = issueLink.getDestinationObject();
		node.addNode(new ReportNode(issue));
	    }
	}

	return node;
    }

    /**
     * Creates html representation of a whole tree uses tree renderer
     * 
     * @return HTML
     */

    private String getHTMLTree(ReportNode node, TreeRenderer renderer) {
	StringBuilder result = new StringBuilder();
	if (node.getChildren().size() == 0) {
	    result.append("<div style=\"text-align:center; font-size:16px;color:red;\">");
	    result.append("Found no issues satisfying your conditions");
	    result.append("</div>");
	} else
	    for (ReportNode child : node.getChildren())
		result.append(getHTML(child, 0, renderer));
	return result.toString();
    }

    /**
     * 
     * @param level
     * @return
     */
    private String getHTML(final ReportNode node, final int level,
	    final TreeRenderer renderer) {

	if (level > 10)
	    return "Nesting level is > 10. Further issues are not shown.";

	final StringBuilder result = new StringBuilder();

	for (ReportNode child : node.getChildren()) 
	    result.append(getHTML(child, level + 1, renderer));
	

	if (renderer == null)
	    return "Tree renderer is not specified";

	return renderer.getHTML(level, node.getIssue(), result.toString(),
		(node.getChildren().size() == 0));
    }

}